package org.dtrevien.metamodel.graphviz.visitor.impl;

import org.dtrevien.metamodel.graphviz.AList;
import org.dtrevien.metamodel.graphviz.AttrList;
import org.dtrevien.metamodel.graphviz.Graph;
import org.dtrevien.metamodel.graphviz.GraphvizModel;
import org.dtrevien.metamodel.graphviz.ID;
import org.dtrevien.metamodel.graphviz.NodeId;
import org.dtrevien.metamodel.graphviz.Port;
import org.dtrevien.metamodel.graphviz.edgerhs.EdgeRHS;
import org.dtrevien.metamodel.graphviz.edgerhs.EdgeRHSNode;
import org.dtrevien.metamodel.graphviz.stmt.Stmt;
import org.dtrevien.metamodel.graphviz.stmt.impl.AttributStmt;
import org.dtrevien.metamodel.graphviz.stmt.impl.Attribute;
import org.dtrevien.metamodel.graphviz.stmt.impl.EdgeStmtNode;
import org.dtrevien.metamodel.graphviz.stmt.impl.EdgeStmtSubgraph;
import org.dtrevien.metamodel.graphviz.stmt.impl.NodeStmt;
import org.dtrevien.metamodel.graphviz.stmt.impl.Subgraph;
import org.dtrevien.metamodel.graphviz.stmt.types.LinkType;
import org.dtrevien.metamodel.graphviz.visitor.Visitor;

/**
 * Generates the Dot Code from a GraphvizModel
 * 
 * @author davidtrevien
 *
 */
public class DotLanguageGenerator extends Visitor {

	protected String declaration = "";
	protected String classAttributes = "";
	protected String classMethods = "";
	protected String links = "";
	protected String str = "";
	
	protected String buffer = "";
	
	@Override
	public void visitAttribute(Attribute a) {
	}

	@Override
	public void visitAttributeStmt(AttributStmt v) {
	}

	@Override
	public void visitEdgeStmtNode(EdgeStmtNode v) {
		String edgeBloc = "";
		String headLabel = "headlabel = \"", tailLabel = "taillabel = \"";
		boolean tail2head = false;
		
		for (EdgeRHS node : v.getEdgeRHSs()) {
			//Check the link Type and setup the needed arrow
			if (v.getLinkType().equals(LinkType.AGGREGATION)) {
				edgeBloc+="arrowhead = \"vee\", arrowtail = \"odiamond\" style=\"solid\"";
			}
			else if (v.getLinkType().equals(LinkType.COMPOSITION)) {
				edgeBloc+="arrowhead = \"vee\", arrowtail = \"diamond\" style=\"solid\"";
			}
			else if (v.getLinkType().equals(LinkType.INHERITANCE)) {
				edgeBloc+="arrowhead = \"none\", arrowtail = \"empty\" style=\"solid\"";
				tail2head = true;
			}
			else if (v.getLinkType().equals(LinkType.IMPLEMENTATION)) {
				edgeBloc+="arrowhead = \"none\", arrowtail = \"empty\" style=\"dashed\"";
				tail2head = true;
			}
			else if (v.getLinkType().equals(LinkType.DIRECT)) {
				edgeBloc+="arrowhead = \"vee\", arrowtail = \"none\" style=\"solid\"";
			}

			//Setup the head label
			if (v.getHeadLabel() != null && !v.getHeadLabel().isEmpty()) {
				headLabel+=v.getHeadLabel();
			}
			
			//Setup the tail label
			if (v.getTailLabel() != null && !v.getTailLabel().isEmpty()) {
				tailLabel+=v.getTailLabel();
			}
			
			this.links+="\tedge ["+edgeBloc+", "+headLabel+"\", "+tailLabel+"\"]\n";
			
			//Check if revert tail and head
			if (tail2head) {
				this.links+="\t"+((EdgeRHSNode) node).getNodeId().getName()
					+" -> "
					+v.getNodeId().getName();
			}
			else {
				this.links+="\t"+v.getNodeId().getName()
				+" -> "
				+((EdgeRHSNode) node).getNodeId().getName();
			}
			
			this.links+="\n";
		}
	}

	@Override
	public void visitEdgeStmtSubgraph(EdgeStmtSubgraph v) {
	}

	@Override
	public void visitNodeStmt(NodeStmt v) {
		//Class name (start bloc)
		this.declaration+="\t"+v.getName()+" [\n\t\tlabel = \"{"+v.getName();
		
		v.getAttrLists().get(0).accept(this); //Attributes
		this.classAttributes = this.buffer;
		this.buffer = "";

		v.getAttrLists().get(1).accept(this); //Methods
		this.classMethods = this.buffer;
		this.buffer = "";

		if (!this.classAttributes.isEmpty()) this.declaration+="| "+this.classAttributes;
		if (!this.classMethods.isEmpty()) this.declaration+="| "+this.classMethods;
		
		this.declaration+="}\"\n\t]\n\n"; //End bloc
	}

	@Override
	public void visitSubgraph(Subgraph v) {
	}

	@Override
	public void visitAList(AList v) {
		this.buffer+=v.getName()+" : "+v.getValue();
	}

	@Override
	public void visitAttrList(AttrList v) {
		//Visit all attributes contained by v
		int i = 0;
		this.buffer = "";
		for (AList a : v.getaLists()) {
			a.accept(this);
			if (i<v.getaLists().size()-1) this.buffer+="\\l";
			i++;
		}
		
	}

	@Override
	public void visitGraph(Graph v) {
		this.str+=v.getType()+" "+v.getName().replace(" ", "")+" {\n";
		this.str+="\tfontname = \"Bitstream Vera Sans\" fontsize = 8\n"+
					"\tnode [\n\t\tfontname = \"Bitstream Vera Sans\"\n\t\tfontsize = 10\n\t\tshape = \"record\"\n\t]\n"+
					"\tedge [\n\t\tfontname = \"Bitstream Vera Sans\"\n\t\tfontsize = 8\n\t\tarrowsize=\"0.7\"\n\t\tdir = \"both\"\n\t]\n";
		
		for (Stmt stmt : v.getStmts()) {
			stmt.accept(this);
		}
		this.str+=this.declaration;
		this.str+=this.links;
		this.str+="}\n";
		
	}

	@Override
	public void visitGraphvizModel(GraphvizModel v) {
		for (Graph g : v.getGraphs()) {
			g.accept(this);
		}
	}

	@Override
	public void visitID(ID v) {
	}

	@Override
	public void visitNodeId(NodeId v) {
	}

	@Override
	public void visitPort(Port v) {
	}

	public String getResult() {
		return this.str;
	}

}
